/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef BASE_INCLUDE_BASE_MEM_H
#define BASE_INCLUDE_BASE_MEM_H

#include <functional>
#include <cstddef>
#include <cstdint>

namespace El {
namespace Utils {

////////////////////////////////////////////////////////////////////////////////

class BuddyInternal;

/**
 * \class Buddy
 * @brief 内存伙伴系统管理类。
 * @details 实现了一个基于伙伴系统算法的内存管理系统，
 *          支持高效的内存分配和释放，有效缓解
 *          内存碎片化问题。提供两种内存管理策略：系统内存和内存池。
 */
class Buddy {
    Buddy();
    Buddy(const Buddy &) = delete;
    Buddy &operator=(const Buddy &) = delete;
    Buddy(Buddy &&) = delete;
    Buddy &operator=(Buddy &&) = delete;

public:
    using AllocProc = std::function<void *(size_t)>; ///< 内存分配函数类型；参数为要分配的大小，返回分配的地址。
    using FreeProc = std::function<void(void *)>;    ///< 内存释放函数类型；参数为 malloc 获取的指针。
    using CopyProc =
        std::function<void *(void *, const void *, size_t)>; ///< 内存拷贝函数类型；参数：目标地址、源地址、大小。

    /// 内存管理策略
    enum class Policy {
        kDefault = 0, ///< 根据平台设定默认策略 (x86 为 System，其他为 Pool)。
        kSystem,      ///< 直接使用系统内存：分配调用 MemoryOperator.malloc，释放调用 MemoryOperator.free。
        kPool,        ///< 内存池模式：预先分配大块内存，按需分配小块以减少系统调用。

        kPolicyCount ///< 哨兵值，非有效策略。
    };

    /// 内存池策略参数
    struct PoolParameter {
        size_t totalSize; ///< 内存池总字节数。由于对齐，实际分配可能更大。默认：2MB。
        size_t chunkSize; ///< 内存块大小（字节），必须是 2^n KB。最小可分配单元。默认：1KB。
        size_t alignSize; ///< 数据区对齐大小（字节），必须是 2^n。默认：4字节。
    };

    /// 自定义内存操作函数集
    struct MemoryOperator {
        AllocProc malloc; ///< 内存池管理的内存分配函数。默认：::malloc。
        FreeProc free;    ///< 内存池管理的内存释放函数。默认：::free。
        CopyProc memcpy;  ///< 内存池管理的内存拷贝函数。默认：::memcpy。
    };

    /**
     * @brief 配置内存管理策略。
     * @details 必须在首次调用 GetInstance() 前调用，否则使用默认设置。
     * @param policy [in] 内存管理策略。
     * @param memop [in] 自定义内存操作符。若为 nullptr，使用默认值。
     * @param param [in] 内存池策略的配置参数。若为 nullptr，使用默认值。
     */
    static void Configure(Policy policy = Policy::kDefault,
                          MemoryOperator *memop = nullptr,
                          PoolParameter *param = nullptr);

    /**
     * @brief 获取 Buddy 单例实例。
     * @return Buddy 单例对象的引用。
     */
    static Buddy &GetInstance();

    /**
     * @brief 析构函数。
     */
    ~Buddy() noexcept;

    /**
     * @brief 获取内存池分配的总内存空间的起始地址。
     * @return 内存池的起始地址，如果未使用内存池策略则返回 nullptr。
     */
    uint8_t *GetPoolBuffer() const;

    /**
     * @brief 从管理的内存中分配内存块。
     * @param size [in] 要分配的内存大小（字节）。
     * @return 指向分配的内存块的指针，失败时返回 nullptr。
     */
    void *Malloc(size_t size);

    /**
     * @brief 释放先前分配的内存块。
     * @param p [in] 从 Malloc 获取的内存块指针。若为 nullptr，则函数不执行任何操作。
     */
    void Free(void *p);

    /**
     * @brief 拷贝内存。
     * @details 如果源和目标都在管理的内存池内，则使用配置的内存拷贝函数，
     *          否则使用标准的 ::memcpy。
     * @param dest [in] 目标内存地址。
     * @param src [in] 源内存地址。
     * @param size [in] 要拷贝的字节数。
     * @return 指向目标内存地址的指针。
     */
    void *Memcpy(void *dest, const void *src, size_t size);

    /**
     * @brief 获取管理的内存总大小（字节）。
     * @details 此值为默认值或通过 Configure() 设置的值。
     * @return 管理的总内存大小（字节）。如果使用系统策略则返回 0。
     */
    size_t GetBufferSize() const;

    /**
     * @brief 获取剩余空闲内存大小（字节）。
     * @details 基于 2^n 页计算，可能因内部碎片而大于用户请求字节的总和。
     * @return 剩余可用内存大小（字节）。如果使用系统策略则返回 0。
     */
    size_t GetFreeSize() const;

    /**
     * @brief 输出内存节点状态，仅用于调试。
     */
    void DumpNodes() const;

private:
    BuddyInternal *internal_; ///< 指向内部实现对象的指针。
};

} // namespace Utils
} // namespace El

#endif // BASE_INCLUDE_BASE_MEM_H
